using System;
using System.Collections.Generic;

namespace Lichtspiel.Utility
{
    /// <summary>
    /// A managed collection of interpolators.
    /// </summary>
    public sealed class InterpolatorCollection : IUpdateable
    {
        private const int InitialPoolSize = 10;

        private readonly List<IPool> pools = new List<IPool>();

        #region IUpdateable Members

        /// <summary>
        /// Updates all active Interpolators in the collection.
        /// </summary>
        public void Update(GameTime time) {
            for (int i = 0; i < pools.Count; i++) {
                IPool interpolators = pools[i];

                lock (interpolators) {
                    for (int j = 0; j < interpolators.ValidCount; j++) {
                        var ip = interpolators.Get(j) as IInterpolator; // sehr hacky....

                        if (ip != null) {
                            ip.Update(time);
                        }
                    }

                    interpolators.CleanUp();
                }
            }
        }

        #endregion

        /// <summary>
        /// Creates a new Interpolator updated and reused by the Collection.
        /// </summary>
        /// <param name="startValue">The starting value.</param>
        /// <param name="endValue">The ending value.</param>
        /// <param name="length">The amount of time, for the interpolation to occur.</param>
        /// <param name="interpolationMethod">How does the interpolation of the value occur.</param>
        /// <param name="step">An optional delegate to invoke each update.</param>        
        /// <param name="completed">An optional delegate to invoke upon completion.</param>
        /// <returns>The Interpolator instance.</returns>
        public Interpolator<T> Create<T>(T startValue, T endValue, TimeSpan length,
                                         Interpolator<T>.InterpolationMethod interpolationMethod,
                                         Action<Interpolator<T>> step, Action<Interpolator<T>> completed) {
            Pool<Interpolator<T>> interpolators = GetPool<T>();

            lock (interpolators) {
                Interpolator<T> i = interpolators.New();
                i.Reset(startValue, endValue, length, interpolationMethod, step, completed);

                return i;
            }
        }

        /// <summary>
        /// Creates a new Interpolator updated and reused by the Collection.
        /// </summary>
        /// <param name="startValue">The starting value.</param>
        /// <param name="endValue">The ending value.</param>
        /// <param name="length">The amount of time, for the interpolation to occur.</param>
        /// <param name="interpolationMethod">How does the interpolation of the value occur.</param>
        /// <param name="step">An optional delegate to invoke each update.</param>        
        /// <returns>The Interpolator instance.</returns>
        public Interpolator<T> Create<T>(T startValue, T endValue, TimeSpan length,
                                         Interpolator<T>.InterpolationMethod interpolationMethod,
                                         Action<Interpolator<T>> step) {
            return Create(startValue, endValue, length, interpolationMethod, step, null);
        }

        /// <summary>
        /// Creates a new Interpolator updated and reused by the Collection.
        /// </summary>
        /// <param name="startValue">The starting value.</param>
        /// <param name="endValue">The ending value.</param>
        /// <param name="length">The amount of time, for the interpolation to occur.</param>
        /// <param name="interpolationMethod">How does the interpolation of the value occur.</param>     
        /// <returns>The Interpolator instance.</returns>
        public Interpolator<T> Create<T>(T startValue, T endValue, TimeSpan length,
                                         Interpolator<T>.InterpolationMethod interpolationMethod) {
            return Create(startValue, endValue, length, interpolationMethod, null);
        }

        private Pool<Interpolator<T>> GetPool<T>() {
            // find pool
            for (int i = 0; i < pools.Count; i++) {
                if (pools[i].GetType() == typeof (Pool<Interpolator<T>>)) {
                    return pools[i] as Pool<Interpolator<T>>;
                }
            }

            // else create and add
            var newPool = new Pool<Interpolator<T>>(InitialPoolSize, i => i.IsActive);
            pools.Add(newPool);
            return newPool;
        }

        public void StopAll() {
            for (int i = 0; i < pools.Count; i++) {
                IPool interpolators = pools[i];

                lock (interpolators) {
                    for (int j = 0; j < interpolators.ValidCount; j++) {
                        var ip = interpolators.Get(j) as IInterpolator; // sehr hacky....

                        if (ip != null) {
                            ip.Stop();
                        }
                    }

                    interpolators.CleanUp();
                }
            }
        }

        public override string ToString() {
            string poolsInfo = "";

            for (int i = 0; i < pools.Count; i++) {
                poolsInfo += "\t" + pools[i] + "\n";
            }

            string ret = "InterpolatorCollection (" + pools.Count + " Pools: " + ")\n";

            return ret + poolsInfo;
        }
    }
}